# coding=utf-8
"""
pygame模块的安装
面向对象分析项目需求
坦克大战项目框架搭建
加载主窗口
坦克大战之事件处理
左上角文字的绘制
加载我方坦克
我方坦克切换方向移动
我方坦克移动优化
我方坦克优化2
加载敌方坦克
敌方坦克随机移动
完善子单类
我方坦克发射子弹
子弹移动
子弹消亡及数量控制
敌方坦克发射子弹
我方子弹与敌方坦克的碰撞
实现爆炸效果
我方坦克的消亡
我方坦克无限重生
加载墙壁
子弹不穿墙
坦克不能穿墙
敌我双方坦克发生碰撞
音效处理
"""
import pygame, time, random
from pygame.sprite import Sprite

SCREEN_WIDTH = 800
SCREEN_HEIGHT = 500
BG_COLOR = pygame.Color(0, 0, 0)
TEXT_COLOR = pygame.Color(255, 0, 0)


class BaseItem(Sprite):
    def __init__(self, color, width, height):
        pygame.sprite.Sprite.__init__(self)


class MainGame():
    window = None
    my_tank = None
    # 存储敌方坦克的列表
    enemyTankList = []
    # 定义生成敌方坦克的数量
    enemyTankCount = 5
    # 存储我方子弹的列表
    myBulletList = []
    # 存储敌方子弹的列表
    enemyBulletList = []
    # 存储爆炸效果的列表
    explodeList = []
    # 存储墙壁的列表
    wallList = []

    def __init__(self):
        pass

    def startGame(self):
        # 加载主窗口
        # 初始化窗口
        pygame.display.init()
        # 设置窗口的大小和显示
        MainGame.window = pygame.display.set_mode([SCREEN_WIDTH, SCREEN_HEIGHT])
        # 初始化我方坦克
        self.createMyTank()
        # 初始化敌方坦克，并将敌方坦克添加到列表中
        self.createEnemyTank()
        # 初始化墙壁
        self.createWall()
        # 设置窗口的标题
        pygame.display.set_caption("坦克大战")
        while True:
            # 使坦克移动的速度慢一点
            time.sleep(0.02)
            # 给窗口设置填充色
            MainGame.window.fill(BG_COLOR)
            # 获取事件
            self.getEvent()
            # 绘制文字
            MainGame.window.blit(self.getTextSuface("敌方坦克剩余数量%d" % len(MainGame.enemyTankList)), (10, 10))
            # 调用显示坦克的方法
            # 判断我方坦克是否是存活
            if MainGame.my_tank and MainGame.my_tank.live:
                MainGame.my_tank.displayTank()
            # 删否则除我方坦克
            else:
                del MainGame.my_tank
                MainGame.my_tank = None
            # 循环遍历敌方坦克列表，展示敌方坦克
            self.blitEnemyTank()
            # 循环遍历显示我方坦克的子弹
            self.blitMyBullet()
            # 循环遍历显示敌方坦克的子弹
            self.blitEnemyBullet()
            # 循环遍历爆炸列表,展示爆炸效果
            self.blitExplode()
            # 循环遍历墙壁列表,展示墙壁
            self.blitWall()
            # 调用坦克移动的方法
            # 如果坦克的开关是开启,才可以移动
            if MainGame.my_tank and MainGame.my_tank.live:
                if not MainGame.my_tank.stop:
                    MainGame.my_tank.move()
                    # 检查我方坦克是否与墙壁发生碰撞
                    MainGame.my_tank.hitWall()
                    # 检测我方坦克是否与敌方坦克发生碰撞
                    MainGame.my_tank.myTank_hit_enemyTank()

            pygame.display.update()

    # 初始化我方坦克
    def createMyTank(self):
        MainGame.my_tank = MyTank(350, 300)
        # 创建music对象
        music = Music("./img/start.wav")
        # 播放音乐
        music.play()

    # 初始化敌方坦克，并将敌方坦克添加到列表中
    def createEnemyTank(self):
        top = 100
        # 循环生成敌方坦克
        for i in range(MainGame.enemyTankCount):
            left = random.randint(0, 600)
            speed = random.randint(0, 4)
            enemy = EnemyTank(left, top, speed)
            MainGame.enemyTankList.append(enemy)

    # 初始化墙壁
    def createWall(self):
        for i in range(6):
            # 初始化墙壁
            wall = Wall(i * 130, 220)
            # 将墙壁添加到列表中
            MainGame.wallList.append(wall)

    # 循环遍历敌方坦克列表，展示敌方坦克
    def blitEnemyTank(self):
        for enemyTank in MainGame.enemyTankList:
            # 判断敌方坦克是否活着
            if enemyTank.live:
                enemyTank.displayTank()
                enemyTank.randMove()
                # 调用检测敌方坦克是否和墙壁碰撞
                enemyTank.hitWall()
                # 检测敌方坦克是否和我方坦克发生碰撞
                if MainGame.my_tank and MainGame.my_tank.live:
                    enemyTank.enemyTank_hit_myTank()
                # 发射子弹
                enemyBullet = enemyTank.shot()
                # 敌方子弹是否为None,如果不为None则添加到敌方子弹列表
                if enemyBullet:
                    # 将敌方坦克子弹存储到敌方子弹列表中
                    MainGame.enemyBulletList.append(enemyBullet)
            # 从敌方坦克列表移除
            else:
                MainGame.enemyTankList.remove(enemyTank)

    # 循环遍历我方坦克的子弹存储列表
    def blitMyBullet(self):
        for myBullet in MainGame.myBulletList:
            # 判断当前子弹是否是活着的状态，如果是则进行显示及移动
            if myBullet.live:
                myBullet.displayBullet()
                # 调用子弹的移动方法
                myBullet.move()
                # 调用检测我方子弹是否和敌方坦克发生碰撞
                myBullet.myBullet_hit_enemyTank()
                # 检测我方子弹是否和墙壁碰撞
                myBullet.hitWall()
            # 否则在列表中删除
            else:
                MainGame.myBulletList.remove(myBullet)

    # 循环遍历显示敌方坦克的子弹
    def blitEnemyBullet(self):
        for enemyBullet in MainGame.enemyBulletList:
            # 判断当前子弹是否是活着的状态，如果是则进行显示及移动
            if enemyBullet.live:
                enemyBullet.displayBullet()
                # 调用子弹的移动方法
                enemyBullet.move()
                # 调用检测敌方子弹是否和我方坦克发生碰撞
                enemyBullet.enemyBullet_hit_myTank()
                # 检测敌方子弹是否和墙壁碰撞
                enemyBullet.hitWall()
            # 否则在列表中删除
            else:
                MainGame.enemyBulletList.remove(enemyBullet)

    # 循环遍历爆炸列表,展示爆炸效果
    def blitExplode(self):
        for explode in MainGame.explodeList:
            # 判断是否是活着的状态，如果是则进行显示
            if explode.live:
                explode.displayExplode()
                # 添加爆炸音效
                music = Music("./img/fire.wav")
                music.play()
            # 否则在爆炸列表中删除
            else:
                MainGame.explodeList.remove(explode)

    # 循环遍历墙壁列表,展示墙壁
    def blitWall(self):
        for wall in MainGame.wallList:
            # 判断墙壁是否存活
            if wall.live:
                # 调用墙壁展示方法
                wall.displayWall()
            else:
                # 从墙壁列表移除
                MainGame.wallList.remove(wall)

    def endGame(self):
        print("感谢使用，欢迎再次使用")
        exit()

    # 左上角文字绘制
    def getTextSuface(self, text):
        # 初始化字体模块
        pygame.font.init()
        # 查看所有字体名称
        # print(pygame.font.get_fonts())
        # 获取字体font对象
        font = pygame.font.SysFont("adobe宋体stdl", 18)
        # 绘制字体信息
        textSurface = font.render(text, True, TEXT_COLOR)
        return textSurface

    def getEvent(self):
        # 获取事件
        eventList = pygame.event.get()
        # 遍历事件
        for event in eventList:
            # 判断按下的键是关闭还是键盘按下
            # 如果按的是退出，关闭窗口
            if event.type == pygame.QUIT:
                self.endGame()
            # 如果是键盘的按下
            if event.type == pygame.KEYDOWN:
                # 当坦克死亡或不存在
                if not MainGame.my_tank:
                    # 如果按下的是ESC键,让我方坦克重生
                    if event.key == pygame.K_ESCAPE:
                        # 让我方坦克重生及调用创建坦克的方法
                        self.createMyTank()
                if MainGame.my_tank and MainGame.my_tank.live:
                    # 判断的是按下的上、下、左、右
                    if event.key == pygame.K_LEFT:
                        # 切换方向
                        MainGame.my_tank.direction = "L"
                        # MainGame.my_tank.move()
                        # 修改坦克的的开关
                        MainGame.my_tank.stop = False
                        print("按下左键，坦克左移")
                    elif event.key == pygame.K_RIGHT:
                        # 切换方向
                        MainGame.my_tank.direction = "R"
                        # MainGame.my_tank.move()
                        # 修改坦克的的开关
                        MainGame.my_tank.stop = False
                        print("按下右键，坦克右移")
                    elif event.key == pygame.K_UP:
                        # 切换方向
                        MainGame.my_tank.direction = "U"
                        # MainGame.my_tank.move()
                        # 修改坦克的的开关
                        MainGame.my_tank.stop = False
                        print("按下上键，坦克上移")
                    elif event.key == pygame.K_DOWN:
                        # 切换方向
                        MainGame.my_tank.direction = "D"
                        # 修改坦克的的开关
                        MainGame.my_tank.stop = False
                        # MainGame.my_tank.move()
                        print("按下下键，坦克下移")
                    elif event.key == pygame.K_SPACE:
                        print("发射子弹")
                        # 如果当前我方子弹列表的大小<3时候才可以创建
                        if len(MainGame.myBulletList) < 3:
                            # 创建我方坦克发射的子弹
                            myBullet = Bullet(MainGame.my_tank)
                            MainGame.myBulletList.append(myBullet)
                            # 我方坦克发射子弹添加音效
                            music = Music("./img/hit.wav")
                            music.play()

            # 松开方向键,坦克移动停止,修改坦克开关状态
            if event.type == pygame.KEYUP:
                # 判断松开的键是上、下、左、右的时候才停止坦克移动
                if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT or event.key == pygame.K_UP or event.key == pygame.K_DOWN:
                    if MainGame.my_tank and MainGame.my_tank.live:
                        MainGame.my_tank.stop = True


# 坦克类
class Tank(BaseItem):
    # 添加距离左边left 距离上边top
    def __init__(self, left, top):
        # 加载保存的图片
        self.images = {
            "U": pygame.image.load("./img/p1tankU.gif"),
            "D": pygame.image.load("./img/p1tankD.gif"),
            "L": pygame.image.load("./img/p1tankL.gif"),
            "R": pygame.image.load("./img/p1tankR.gif"),
        }
        # 方向
        self.direction = "U"
        # 根据当前图片的方向获取图片
        self.image = self.images[self.direction]
        # 根据图片获取区域
        self.rect = self.image.get_rect()
        # 设置区域的left和top
        self.rect.left = left
        self.rect.top = top
        # 速度决定移动的快慢
        self.speed = 5
        # 坦克移动的开关
        self.stop = True
        # 是否存活
        self.live = True
        # 新增属性原来坐标
        self.oldLeft = self.rect.left
        self.oldTop = self.rect.top

    # 移动
    def move(self):
        # 移动后记录原始的坐标
        self.oldLeft = self.rect.left
        self.oldTop = self.rect.top
        # 判断坦克的方向进行移动
        if self.direction == "L":
            # 判断是否超过边界
            if self.rect.left > 0:
                self.rect.left -= self.speed
        elif self.direction == "R":
            # 判断是否超过边界
            if self.rect.left + self.rect.height < SCREEN_WIDTH:
                self.rect.left += self.speed
        elif self.direction == "U":
            # 判断是否超过边界
            if self.rect.top > 0:
                self.rect.top -= self.speed
        elif self.direction == "D":
            # 判断是否超过边界
            if self.rect.top + self.rect.height < SCREEN_HEIGHT:
                self.rect.top += self.speed

    # 射击
    def shot(self):
        return Bullet(self)

    def stay(self):
        self.rect.left = self.oldLeft
        self.rect.top = self.oldTop

    # 检查坦克是否和墙壁发生碰撞
    def hitWall(self):
        for wall in MainGame.wallList:
            if pygame.sprite.collide_rect(self, wall):
                # 将坐标设置移动之前的坐标
                self.stay()

    # 展示坦克的方法
    def displayTank(self):
        # 获取展示对象
        self.image = self.images[self.direction]
        # 调用blit方法展示
        MainGame.window.blit(self.image, self.rect)


# 我方坦克
class MyTank(Tank):
    def __init__(self, left, top):
        super(MyTank, self).__init__(left, top)

    # 检测我方坦克是否与敌方坦克发生碰撞
    def myTank_hit_enemyTank(self):
        # 循环遍历敌方坦克列表
        for enemyTank in MainGame.enemyTankList:
            if pygame.sprite.collide_rect(self, enemyTank):
                self.stay()


# 敌方坦克
class EnemyTank(Tank):
    def __init__(self, left, top, speed):
        # 调用父类的初始化方法
        super(EnemyTank, self).__init__(left, top)
        # 加载啊图片集
        self.images = {
            "U": pygame.image.load("./img/enemy1U.gif"),
            "D": pygame.image.load("./img/enemy1D.gif"),
            "R": pygame.image.load("./img/enemy1R.gif"),
            "L": pygame.image.load("./img/enemy1L.gif"),
        }
        # 方向，随机生成敌方坦克的方向
        self.direction = self.randDirection()
        # 根据方向获取图片
        self.image = self.images[self.direction]
        # 根据图片获取区域
        self.rect = self.image.get_rect()
        # 设置区域的left和top
        self.rect.left = left
        self.rect.top = top
        # 速度决定移动的快慢
        self.speed = 5
        # 坦克移动的开关
        self.stop = True
        # 新增加一个步数变量 step
        self.step = 50

    # 随机生成敌方坦克的方向
    def randDirection(self):
        num = random.randint(1, 4)
        if num == 1:
            return "U"
        elif num == 2:
            return "D"
        elif num == 3:
            return "L"
        elif num == 4:
            return "R"

    def randMove(self):
        if self.step <= 0:
            # 修改方向
            self.direction = self.randDirection()
            # 让步数复位
            self.step = 50
        else:
            self.move()
            # 让步数递减
            self.step -= 1

    # 重写shot()
    def shot(self):
        # 随机生成100以内的数
        num = random.randint(1, 100)
        if num < 10:
            return Bullet(self)

    # 检测敌方坦克是否和我方坦克发生碰撞
    def enemyTank_hit_myTank(self):
        if pygame.sprite.collide_rect(self, MainGame.my_tank):
            self.stay()


# 子弹类
class Bullet(BaseItem):
    def __init__(self, tank):
        # 加载图片
        self.image = pygame.image.load("./img/enemymissile.gif")
        # 坦克的方向决定子弹的方向
        self.direction = tank.direction
        # 获取区域
        self.rect = self.image.get_rect()
        # 子弹的left和top和方向有关
        if self.direction == "U":
            self.rect.left = tank.rect.left + tank.rect.width / 2 - self.rect.width / 2
            self.rect.top = tank.rect.top - self.rect.height
        elif self.direction == "D":
            self.rect.left = tank.rect.left + tank.rect.width / 2 - self.rect.width / 2
            self.rect.top = tank.rect.top + self.rect.height
        elif self.direction == "R":
            self.rect.left = tank.rect.left + self.rect.width
            self.rect.top = tank.rect.top + tank.rect.width / 2 - self.rect.width / 2
        elif self.direction == "L":
            self.rect.left = tank.rect.left - tank.rect.width / 2 - self.rect.width / 2
            self.rect.top = tank.rect.top + tank.rect.width / 2 - self.rect.width / 2
        # 子弹的速度
        self.speed = 6
        # 子弹的状态，是否碰到墙壁，如果碰到墙壁，修改此状态
        self.live = True

    # 移动
    def move(self):
        # 子弹的移动和方向有关
        if self.direction == "U":
            if self.rect.top > 0:
                self.rect.top -= self.speed
            else:
                # 修改子弹状态
                self.live = False
        elif self.direction == "D":
            if self.rect.top + self.rect.height < SCREEN_HEIGHT:
                self.rect.top += self.speed
            else:
                # 修改子弹状态
                self.live = False
        elif self.direction == "R":
            if self.rect.left + self.rect.width < SCREEN_WIDTH:
                self.rect.left += self.speed
            else:
                # 修改子弹状态
                self.live = False
        elif self.direction == "L":
            if self.rect.left > 0:
                self.rect.left -= self.speed
            else:
                # 修改子弹状态
                self.live = False

                # 展示子弹的方法

    # 子弹是否碰撞墙壁
    def hitWall(self):
        for wall in MainGame.wallList:
            if pygame.sprite.collide_rect(self, wall):
                # 修改子弹的生存状态,让子弹消失
                self.live = False
                # 墙壁的生命值减小
                wall.hp -= 1
                if wall.hp < 0:
                    # 修改墙壁的生存状态
                    wall.live = False

    def displayBullet(self):
        # 将图片surface加载到窗口
        MainGame.window.blit(self.image, self.rect)

    # 我方子弹和敌方坦克碰撞
    def myBullet_hit_enemyTank(self):
        # 循环遍历敌方坦克列表,判断是否发生碰撞
        for enemyTank in MainGame.enemyTankList:
            if pygame.sprite.collide_rect(enemyTank, self):
                # 修改敌方坦克和我方子弹的状态
                enemyTank.live = False
                self.live = False
                # 创建爆炸对象
                explode = Expode(enemyTank)
                # 将爆炸对象添加到爆炸列表中
                MainGame.explodeList.append(explode)

    # 敌方子弹和我方坦克碰撞
    def enemyBullet_hit_myTank(self):
        if MainGame.my_tank and MainGame.my_tank.live:
            if pygame.sprite.collide_rect(MainGame.my_tank, self):
                # 创建爆炸对象
                explode = Expode(MainGame.my_tank)
                # 将爆炸对象添加到爆炸列表中
                MainGame.explodeList.append(explode)
                # 修改敌方子弹和我方坦克的状态
                self.live = False
                MainGame.my_tank.live = False


# 墙壁类
class Wall():
    def __init__(self, left, top):
        # 加载墙壁图片
        self.image = pygame.image.load("./img/steels.gif")
        # 获取墙壁区域
        self.rect = self.image.get_rect()
        # 设置位置left和top
        self.rect.left = left
        self.rect.top = top
        # 是否存活
        self.live = True
        # 设置生命值
        self.hp = 3

    # 展示墙壁的方法
    def displayWall(self):
        MainGame.window.blit(self.image, self.rect)


# 爆炸类
class Expode():
    def __init__(self, tank):
        # 爆炸位置由当前子弹打中的坦克位置决定
        self.rect = tank.rect
        self.images = [
            pygame.image.load("./img/blast0.gif"),
            pygame.image.load("./img/blast1.gif"),
            pygame.image.load("./img/blast2.gif"),
            pygame.image.load("./img/blast3.gif"),
            pygame.image.load("./img/blast4.gif"),
        ]
        self.step = 0
        self.image = self.images[self.step]
        # 是否活着
        self.live = True

    # 展示爆炸效果的方法
    def displayExplode(self):
        if self.step < len(self.images):
            # 根据索引获取爆炸对象
            self.image = self.images[self.step]
            self.step += 1
            # 添加到主窗口
            MainGame.window.blit(self.image, self.rect)
        else:
            # 修改活着的状态
            self.live = False
            self.step = 0


# 音效类
class Music():
    def __init__(self, filename):
        self.filename = filename
        # 初始化音乐混合器
        pygame.mixer.init()
        # 加载音乐
        pygame.mixer.music.load(self.filename)

    # 播放音乐的方法
    def play(self):
        pygame.mixer.music.play()


if __name__ == '__main__':
    MainGame().startGame()
    # MainGame().getTextSuface()
